#region License, Terms and Conditions
//
// Prawks - Copyright (c) 2007 Atif Aziz. All rights reserved.
//
// This library is free software; you can redistribute it and/or modify it under
// the terms of the GNU Lesser General Public License as published by the Free
// Software Foundation; either version 2.1 of the License, or (at your option)
// any later version.
//
// This library is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
// FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
// details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with this library; if not, write to the Free Software Foundation, Inc.,
// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 
//
#endregion

namespace Prawks.SqlMethods
{
    using System;
    using System.Data;
    using System.Data.SqlClient;
    using CodeProvisioning;
    using Prawks.DbMethods;

    using MethodInfo = System.Reflection.MethodInfo;
    using ParameterInfo = System.Reflection.ParameterInfo;
    using IDeserializationCallback = System.Runtime.Serialization.IDeserializationCallback;
    
    /// <summary>
    /// Supplies the implementation for a method that has been marked with
    /// the attribute <see cref="SqlOutMethodAttribute"/>. This class may not be
    /// inherited.
    /// </summary>
    
    [ Serializable ]
    public sealed class SqlOutMethod : LogicalMethodImpl, IDbOutMethod, IDeserializationCallback
    {
        [ NonSerialized ] private string[] _outParamNames;

        /// <summary>
        /// Initializes a new instance of the <see cref="SqlOutMethod"/> class with
        /// the method for which it provides the implementation.
        /// </summary>
        /// <param name="method">
        /// The method to which this instance is bound.
        /// </param>

        public SqlOutMethod(MethodInfo method) : base(method)
        {
            Initialize();
        }

        void IDeserializationCallback.OnDeserialization(object sender)
        {
            Initialize();
        }

        private void Initialize()
        {
            DbMethodReflector.ValidateOutMethodSignature(Method);

            ParameterInfo[] parameters = Method.GetParameters();

            int outParamsCount = parameters.Length - 1;
            
            _outParamNames = new string[outParamsCount];

            for (int i = 0; i < outParamsCount; i++)
            {
                ParameterInfo parameter = parameters[i + 1];

                SqlParameterAttributeBase attribute = (SqlParameterAttributeBase) Attribute.GetCustomAttribute(parameter, typeof(SqlParameterAttributeBase));

                string sqlParameterName = null;
                
                if (attribute != null && attribute.IsNameDefined)
                    sqlParameterName = attribute.Name;

                if (sqlParameterName == null)
                    sqlParameterName = "@" + parameter.Name;

                _outParamNames[i] = sqlParameterName;
            }
        }

        /// <summary>
        /// Returns the output parameter values of a <see cref="SqlCommand"/> based 
        /// on the signature of <see cref="IMethodImpl.Method"/>.
        /// </summary>
        /// <returns>
        /// Returns an array of output parameter values. The order of the value is
        /// dictated by the signature of <see cref="IMethodImpl.Method"/>.
        /// </returns>
        /// <param name="command">
        /// The <see cref="SqlCommand"/> whose output parameter values are to be 
        /// returned.
        /// </param>
        
        public object[] GetCommandOutputs(SqlCommand command)
        {
            if (command == null)
                throw new ArgumentNullException("command");

            object[] values = new object[_outParamNames.Length];

            for (int i = 0; i < _outParamNames.Length; i++)
                values[i] = command.Parameters[_outParamNames[i]].Value;

            return values;
        }

        object[] IDbOutMethod.GetCommandOutputs(IDbCommand command)
        {
            return GetCommandOutputs((SqlCommand) command);
        }

        /// <summary>
        /// Invokes the implementation as a logical call.
        /// </summary>
        /// <param name="call">
        /// The call state captured at the call site.
        /// </param>
        /// <remarks>
        /// This method is called internally by <see cref="LogicalMethodImpl"/>.
        /// </remarks>

        protected override void OnInvoke(LogicalCall call)
        {
            SqlCommand command = (SqlCommand) call.GetParameterValue(0);

            for (int i = 0; i < _outParamNames.Length; i++)
                call.SetParameterValue(i + 1, command.Parameters[_outParamNames[i]].Value);
        }
    }
}
